package com.sc.nft.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sc.nft.dao.*;
import com.sc.nft.entity.*;
import com.sc.nft.entity.vo.PushManageTagUserVo;
import com.sc.nft.entity.vo.PushManageTagVo;
import com.sc.nft.enums.PushTagLinkTypeEnum;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.service.PushTagService;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.sup.PageResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 刘国航
 * @desc
 * @since 2024/6/14 11:54
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class PushTagServiceImpl implements PushTagService {

    private final PushTagDao pushTagDao;
    private final PushTagRelationUserDao pushTagRelationUserDao;
    private final UserInfoDao userInfoDao;
    private final DigitalCollectionDao digitalCollectionDao;
    private final UserCollectionDao userCollectionDao;

    @Override
    public void addTagByCollection(String name, Long collectionId) {
        PushTag pushTag = pushTagDao.getByName(name);
        if(Objects.nonNull(pushTag)) {
            throw new GlobalRunTimeException("标签名称已存在");
        }
        pushTag = new PushTag();
        pushTag.setName(name);
        pushTag.setCollectionId(collectionId);
        pushTag.setLinkType(PushTagLinkTypeEnum.RELATION_COLLECTION);
        pushTag.insert();
    }

    @Override
    public List<String> uploadTagUserExcel(MultipartFile uploadFile) throws IOException {
        List<String> userTelList = handleExcelData(uploadFile);
        checkUserTel(userTelList);
        return userTelList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addTagByExcel(String name, List<String> userTelList) throws IOException {
        if(CollectionUtil.isEmpty(userTelList)) {
            throw new GlobalRunTimeException("手机号不能为空");
        }
        Map<String, UserInfo> userInfoMap = checkUserTel(userTelList);
        PushTag pushTag = pushTagDao.getByName(name);
        if(Objects.nonNull(pushTag)) {
            throw new GlobalRunTimeException("标签名称已存在");
        }
        pushTag = new PushTag();
        pushTag.setName(name);
        pushTag.setLinkType(PushTagLinkTypeEnum.RELATION_USER);
        pushTag.insert();
        Long pushTagId = pushTag.getId();
        userInfoMap.forEach((key, value) -> {
            PushTagRelationUser pushTagRelationUser = new PushTagRelationUser();
            pushTagRelationUser.setPushTagId(pushTagId);
            pushTagRelationUser.setUserId(value.getId());
            pushTagRelationUser.setUserTel(value.getUserTel());
            pushTagRelationUser.insert();
        });
    }

    @Override
    public PageResult<PushManageTagVo> tagListPage(Integer pageNo, Integer pageSize, String name) {
        Page<PushTag> pushTagPage = pushTagDao.pageByName(new Page<>(pageNo, pageSize), name);
        List<PushTag> records = pushTagPage.getRecords();
        List<PushManageTagVo> pushManageTagVoList = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(records)) {
            List<Long> collectionIdList = records.stream().filter(pushTag -> Objects.equals(pushTag.getLinkType().getCode(),PushTagLinkTypeEnum.RELATION_COLLECTION.getCode())).map(PushTag::getCollectionId).distinct().collect(Collectors.toList());
            List<Long> pushTagIdList = records.stream().filter(pushTag -> Objects.equals(pushTag.getLinkType().getCode(),PushTagLinkTypeEnum.RELATION_USER.getCode())).map(PushTag::getId).collect(Collectors.toList());

            Map<Long, DigitalCollection> digitalCollectionMap = new HashMap<>();
            Map<Long, List<UserCollection>> userCollectionMap = new HashMap<>();
            Map<Long, List<PushTagRelationUser>> pushTagRelationUserMap = new HashMap<>();

            if(CollectionUtil.isNotEmpty(collectionIdList)) {
                List<DigitalCollection> digitalCollectionList = digitalCollectionDao.listByIds(collectionIdList);
                List<UserCollection> userCollectionList = userCollectionDao.getListByCollectionIds(collectionIdList);
                if(CollectionUtil.isNotEmpty(digitalCollectionList)) {
                    digitalCollectionMap = digitalCollectionList.stream().collect(Collectors.toMap(DigitalCollection::getId, Function.identity()));
                }
                if(CollectionUtil.isNotEmpty(userCollectionList)) {
                    userCollectionMap = userCollectionList.stream().collect(Collectors.groupingBy(UserCollection::getCollectionId));
                }
            }

            if(CollectionUtil.isNotEmpty(pushTagIdList)) {
                List<PushTagRelationUser> pushTagRelationUserList = pushTagRelationUserDao.getListByPushTagIdList(pushTagIdList);
                if(CollectionUtil.isNotEmpty(pushTagRelationUserList)) {
                    pushTagRelationUserMap = pushTagRelationUserList.stream().collect(Collectors.groupingBy(PushTagRelationUser::getPushTagId));
                }
            }

            for(PushTag record : records) {
                PushManageTagVo pushManageTagVo = new PushManageTagVo();
                pushManageTagVo.setId(record.getId());
                pushManageTagVo.setName(record.getName());
                pushManageTagVo.setLinkType(record.getLinkType());
                if(Objects.equals(record.getLinkType().getCode(), PushTagLinkTypeEnum.RELATION_COLLECTION.getCode())) {
                    pushManageTagVo.setCollectionId(record.getCollectionId());
                    DigitalCollection digitalCollection = digitalCollectionMap.get(record.getCollectionId());
                    if(Objects.nonNull(digitalCollection)) {
                        pushManageTagVo.setCollectionName(digitalCollection.getFirstTitle());
                        pushManageTagVo.setCollectionImage(digitalCollection.getCreationContent());
                        List<UserCollection> userCollectionList = userCollectionMap.get(record.getCollectionId());
                        if(CollectionUtil.isNotEmpty(userCollectionList)) {
                            pushManageTagVo.setUserCount(userCollectionList.size());
                        }
                    }
                } else if(Objects.equals(record.getLinkType().getCode(), PushTagLinkTypeEnum.RELATION_USER.getCode())) {
                    List<PushTagRelationUser> pushTagRelationUserList = pushTagRelationUserMap.get(record.getId());
                    if(CollectionUtil.isNotEmpty(pushTagRelationUserList)) {
                        pushManageTagVo.setUserCount(pushTagRelationUserList.size());
                    }
                }
                pushManageTagVoList.add(pushManageTagVo);
            }

        }
        PageResult<PushManageTagVo> pushManageTagVoPageResult = new PageResult<>();
        pushManageTagVoPageResult.setCurrentPage(pushTagPage.getCurrent());
        pushManageTagVoPageResult.setTotalCount(pushTagPage.getTotal());
        pushManageTagVoPageResult.setPageSize(pushTagPage.getSize());
        pushManageTagVoPageResult.setData(pushManageTagVoList);
        pushManageTagVoPageResult.setCode(ErrorCode.SUCCESS.getCode());
        return pushManageTagVoPageResult;
    }

    @Override
    public PushManageTagVo tagDetail(Integer pageNo, Integer pageSize, Long id, String userTel) {
        PushTag pushTag = pushTagDao.getById(id);
        if(Objects.isNull(pushTag)) {
            return null;
        }
        PushManageTagVo pushManageTagVo = new PushManageTagVo();
        pushManageTagVo.setId(pushTag.getId());
        pushManageTagVo.setName(pushTag.getName());
        pushManageTagVo.setLinkType(pushTag.getLinkType());
        if(Objects.equals(pushTag.getLinkType().getCode(), PushTagLinkTypeEnum.RELATION_COLLECTION.getCode())) {
            pushManageTagVo.setCollectionId(pushTag.getCollectionId());
            DigitalCollection digitalCollection = digitalCollectionDao.getById(pushTag.getCollectionId());
            if(Objects.nonNull(digitalCollection)) {
                pushManageTagVo.setCollectionName(digitalCollection.getFirstTitle());
                pushManageTagVo.setCollectionImage(digitalCollection.getCreationContent());
                List<Long> collectionIdList = new ArrayList<>();
                collectionIdList.add(pushTag.getCollectionId());
                List<UserCollection> userCollectionList = userCollectionDao.getListByCollectionIds(collectionIdList);
                if(CollectionUtil.isNotEmpty(userCollectionList)) {
                    pushManageTagVo.setUserCount(userCollectionList.size());
                }
            }
        } else if(Objects.equals(pushTag.getLinkType().getCode(), PushTagLinkTypeEnum.RELATION_USER.getCode())) {
            List<Long> pushTagIdList = new ArrayList<>();
            pushTagIdList.add(pushTag.getId());
            List<PushTagRelationUser> pushTagRelationUserList = pushTagRelationUserDao.getListByPushTagIdList(pushTagIdList);
            if(CollectionUtil.isNotEmpty(pushTagRelationUserList)) {
                pushManageTagVo.setUserCount(pushTagRelationUserList.size());
            }
        }

        Page<PushTagRelationUser> pushTagRelationUserPage = pushTagRelationUserDao.pageByTagIdAndTel(new Page<>(pageNo, pageSize), pushTag.getId(), userTel);
        List<PushTagRelationUser> records = pushTagRelationUserPage.getRecords();

        List<PushManageTagUserVo> pushManageTagUserVoList = new ArrayList<>();

        if(CollectionUtil.isNotEmpty(records)) {
            List<Long> userIdList = records.stream().map(PushTagRelationUser::getUserId).distinct().collect(Collectors.toList());
            List<UserInfo> userInfoList = userInfoDao.getByIdList(userIdList);
            Map<Long, UserInfo> userInfoMap = CollectionUtil.isNotEmpty(userInfoList) ? userInfoList.stream().collect(Collectors.toMap(UserInfo::getId, Function.identity())) : new HashMap<>();
            for(PushTagRelationUser record : records) {
                PushManageTagUserVo pushManageTagUserVo = new PushManageTagUserVo();
                pushManageTagUserVo.setUserId(record.getUserId());
                pushManageTagUserVo.setUserTel(record.getUserTel());
                pushManageTagUserVo.setNickName(Objects.nonNull(userInfoMap.get(record.getUserId())) ? userInfoMap.get(record.getUserId()).getNickName() : "");
                pushManageTagUserVoList.add(pushManageTagUserVo);
            }
        }

        Page<PushManageTagUserVo> pushManageTagUserVoPage = new Page<>();
        pushManageTagUserVoPage.setCurrent(pushTagRelationUserPage.getCurrent());
        pushManageTagUserVoPage.setTotal(pushTagRelationUserPage.getTotal());
        pushManageTagUserVoPage.setSize(pushTagRelationUserPage.getSize());
        pushManageTagUserVoPage.setRecords(pushManageTagUserVoList);
        pushManageTagVo.setUserData(pushManageTagUserVoPage);
        return pushManageTagVo;
    }

    @Override
    public List<PushManageTagVo> allTag() {
        List<PushTag> pushTagList = pushTagDao.all();
        List<PushManageTagVo> pushManageTagVoList = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(pushTagList)) {
            for(PushTag pushTag : pushTagList) {
                PushManageTagVo pushManageTagVo = new PushManageTagVo();
                pushManageTagVo.setId(pushTag.getId());
                pushManageTagVo.setName(pushTag.getName());
                pushManageTagVo.setLinkType(pushTag.getLinkType());
                pushManageTagVo.setCollectionId(pushTag.getCollectionId());
                pushManageTagVoList.add(pushManageTagVo);
            }
        }
        return pushManageTagVoList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void tagDelete(Long id) {
        PushTag pushTag = pushTagDao.getById(id);
        if(Objects.isNull(pushTag)) {
            throw new GlobalRunTimeException("标签不存在");
        }
        pushTagDao.removeById(pushTag.getId());
        if(Objects.equals(pushTag.getLinkType().getCode(), PushTagLinkTypeEnum.RELATION_USER.getCode())) {
            List<PushTagRelationUser> pushTagRelationUserList = pushTagRelationUserDao.getListByPushTagId(pushTag.getId());
            if(CollectionUtil.isNotEmpty(pushTagRelationUserList)) {
                for(PushTagRelationUser pushTagRelationUser : pushTagRelationUserList) {
                    pushTagRelationUserDao.removeById(pushTagRelationUser.getId());
                }
            }
        }
    }

    @Override
    public void tagAddUser(Long id, List<String> userTelList) {
        if(CollectionUtil.isEmpty(userTelList)) {
            throw new GlobalRunTimeException("手机号不能为空");
        }
        PushTag pushTag = pushTagDao.getById(id);
        if(Objects.isNull(pushTag)) {
            throw new GlobalRunTimeException("标签不存在");
        }
        if(Objects.equals(pushTag.getLinkType().getCode(), PushTagLinkTypeEnum.RELATION_COLLECTION.getCode())) {
            throw new GlobalRunTimeException("标签已关联藏品，不能添加用户");
        }
        Map<String, UserInfo> userInfoMap = checkUserTel(userTelList);
        List<UserInfo> userInfoList = new ArrayList<>(userInfoMap.values());
        List<Long> userIdList = userInfoList.stream().map(UserInfo::getId).collect(Collectors.toList());
        List<PushTagRelationUser> pushTagRelationUserList = pushTagRelationUserDao.getListByPushTagIdAndUserIdList(pushTag.getId(), userIdList);
        List<PushTagRelationUser> addPushTagRelationUserList = new ArrayList<>();
        List<PushTagRelationUser> updatePushTagRelationUserList = new ArrayList<>();

        userInfoList.forEach(userInfo -> {
            Optional<PushTagRelationUser> optional = CollectionUtil.isNotEmpty(pushTagRelationUserList)
                    ? pushTagRelationUserList.stream().filter(pushTagRelationUser -> Objects.equals(pushTagRelationUser.getUserId(), userInfo.getId())).findFirst()
                    : Optional.empty();
            if(optional.isPresent()) {
                PushTagRelationUser pushTagRelationUser = optional.get();
                pushTagRelationUser.setUserTel(userInfo.getUserTel());
                updatePushTagRelationUserList.add(pushTagRelationUser);
            } else {
                PushTagRelationUser pushTagRelationUser = new PushTagRelationUser();
                pushTagRelationUser.setPushTagId(pushTag.getId());
                pushTagRelationUser.setUserId(userInfo.getId());
                pushTagRelationUser.setUserTel(userInfo.getUserTel());
                addPushTagRelationUserList.add(pushTagRelationUser);
            }
        });
        if(CollectionUtil.isNotEmpty(addPushTagRelationUserList)) {
            pushTagRelationUserDao.saveBatch(addPushTagRelationUserList);
        }
        if(CollectionUtil.isNotEmpty(updatePushTagRelationUserList)) {
            pushTagRelationUserDao.updateBatchById(updatePushTagRelationUserList);
        }
    }

    @Override
    public void tagDeleteUser(Long id, Long userId) {
        PushTag pushTag = pushTagDao.getById(id);
        if(Objects.isNull(pushTag)) {
            throw new GlobalRunTimeException("标签不存在");
        }
        if(Objects.equals(pushTag.getLinkType().getCode(), PushTagLinkTypeEnum.RELATION_COLLECTION.getCode())) {
            throw new GlobalRunTimeException("标签已关联藏品，不能删除用户");
        }
        PushTagRelationUser pushTagRelationUser = pushTagRelationUserDao.getOneByPushTagIdAndUserId(id, userId);
        if(Objects.isNull(pushTagRelationUser)) {
            throw new GlobalRunTimeException("标签下用户不存在");
        }
        pushTagRelationUserDao.removeById(pushTagRelationUser.getId());
    }

    /**
     * 处理上传EXCEL文件
     *
     * @param uploadFile
     */
    private List<String> handleExcelData(MultipartFile uploadFile) throws IOException {
        String fileName = uploadFile.getOriginalFilename();
        String substring = fileName.substring(fileName.lastIndexOf("."));
        InputStream inputStream = uploadFile.getInputStream();
        Workbook wb = null;
        if (".xls".equals(substring)) {
            wb = new HSSFWorkbook(inputStream);
        } else if (".xlsx".equals(substring)) {
            wb = new XSSFWorkbook(inputStream);
        } else {
            throw new GlobalRunTimeException(ErrorCode.WRONG_FILE_TYPE);
        }
        Sheet sheet = wb.getSheetAt(0);
        //获取最大行数
        int rownum = sheet.getPhysicalNumberOfRows();
        Map<String,Integer> userTelMap = new HashMap<>();
        for (int i = 1; i < rownum; i++) {
            Row row = sheet.getRow(i);
            Cell phoneCell = row.getCell(0);
            if (ObjectUtil.isNotEmpty(phoneCell)) {
                phoneCell.setCellType(CellType.STRING);
                String userTel = phoneCell.getStringCellValue().trim();
                if(StrUtil.isNotBlank(userTel)) {
                    userTelMap.put(userTel, (Objects.nonNull(userTelMap.get(userTel)) ? userTelMap.get(userTel) : 0) + 1);
                    Cell nickNameCell = row.getCell(1);
                    if (ObjectUtil.isNotEmpty(nickNameCell)) {
                        nickNameCell.setCellType(CellType.STRING);
                    }
                }
            }
        }
        if(userTelMap.isEmpty()) {
            throw new GlobalRunTimeException("手机号不能为空");
        }
        userTelMap.forEach((key, value) -> {
            if(value > 1) {
                throw new GlobalRunTimeException("手机号" + key + "重复");
            }
        });
        return new ArrayList<>(userTelMap.keySet());
    }

    /**
     * 校验手机号
     * @param userTelList
     */
    private Map<String, UserInfo> checkUserTel(List<String> userTelList) {
        List<UserInfo> userInfoList = userInfoDao.getListByTelList(userTelList);
        if(ObjectUtil.isEmpty(userInfoList)){
            throw new GlobalRunTimeException("手机号不存在");
        }
        Map<String, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getUserTel, Function.identity()));
        userTelList.forEach(userTel -> {
            UserInfo userInfo = userInfoMap.get(userTel);
            if(ObjectUtil.isEmpty(userInfo)){
                throw new GlobalRunTimeException("手机号" + userTel + "不存在");
            }
        });
        return userInfoMap;
    }

}
